/*
WhirlMon - Whirlpool.net forums thread monitor
Copyright (C) 2012  Lindsay Mathieson

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

/**
 * 
 */
package org.dyndns.blackpaw.whirlmon;

import java.util.Timer;
import java.util.TimerTask;

import org.dyndns.blackpaw.whirlmon.Json.WhirlpoolException;
import org.dyndns.blackpaw.whirlmon.Json.WhirlpoolNullAPIException;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.preference.PreferenceManager;
import android.util.Log;

;

/**
 * @author lindsay
 * 
 */
public class MainService extends Service {
	final static String LOGTAG = "WHIRLSVC";
	final static int ID_MSG_POLL = 1;
	final static int ID_MSG_DO_REFRESH = 2;
	final static int ID_MSG_DO_GET = 3;
	final static int ID_MSG_DO_MARK_READ = 4;

	public final static int ID_NM_NEW_MSG = 1;
	final static int ID_NM_ERROR = 2;

	private Looper mServiceLooper;
	private ServiceHandler mServiceHandler;
	private Timer mTimer;

	/**
	 * 
	 */
	public MainService() {
	}

	@Override
	public void onCreate() {
		Log.i(LOGTAG, "onCreate");
		try {
			HandlerThread thread = new HandlerThread("ServiceStartArguments",
					Process.THREAD_PRIORITY_BACKGROUND);
			thread.start();

			// Get the HandlerThread's Looper and use it for our Handler
			mServiceLooper = thread.getLooper();
			mServiceHandler = new ServiceHandler(mServiceLooper);

			SharedPreferences sharedPrefs = PreferenceManager
					.getDefaultSharedPreferences(this);
			long pollInterval = Long.parseLong(sharedPrefs.getString(
					"poll_interval", "5"));
			SetPollInterval(pollInterval);
		} catch (Exception x) {
			Log.e(LOGTAG, "onCreate", x);
		}
	}

	// ///////////////////////////
	// Binding
	// Binder given to clients
	private final IBinder mBinder = new MainBinder();
	private IClientBinder mClient = null;

	public class MainBinder extends Binder {
		public void SetClient(IClientBinder client) {
			mClient = client;
		}

		public void DoRefresh() {
			Log.i(LOGTAG, "MainBinder.DoRefresh()");
			Message msg = mServiceHandler.obtainMessage();
			msg.arg1 = ID_MSG_DO_REFRESH;
			mServiceHandler.sendMessage(msg);
		}

		public void MarkThreadRead(long id) {
			Log.i(LOGTAG, "MainBinder.MarkThreadRead(" + id + ")");
			Message msg = mServiceHandler.obtainMessage();
			msg.arg1 = ID_MSG_DO_MARK_READ;
			msg.arg2 = (int) id;
			mServiceHandler.sendMessage(msg);
		}

		public void GetCurrent() {
			Log.i(LOGTAG, "MainBinder.DoGetCurrent()");
			Message msg = mServiceHandler.obtainMessage();
			msg.arg1 = ID_MSG_DO_GET;
			mServiceHandler.sendMessage(msg);
		}
	}

	// Call back Class clients give to us
	public interface IClientBinder {
		void UpdateWatchedThreads(WPThread.List watched);

		void ShowProgressBar(boolean show);
	}

	@Override
	public IBinder onBind(Intent arg0) {
		Log.i(LOGTAG, "onBind");
		cancelNotification(ID_NM_NEW_MSG);
		return mBinder;
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		Log.i(LOGTAG, "onStartCommand");

		// If we get killed, after returning from here, restart
		return START_STICKY;
	}

	@Override
	public void onDestroy() {
		Log.i(LOGTAG, "onDestroy");
		mTimer.cancel();
		super.onDestroy();
	}

	long lastPollMin = -1;

	void SetPollInterval(long min) {
		// Sanity check
		// disallow anything less that 5 min thats not zero.
		if (min < 0)
			min = 0;
		else if (min > 0 && min < 5)
			min = 5;

		// Cancel timer?
		if (min == 0) {
			if (mTimer != null) {
				mTimer.cancel();
				mTimer = null;
				Log.i(LOGTAG, "Polling disabled");
			}
			lastPollMin = 0;
			return;
		}
		// Convert minutes to milliseconds
		// Same as existing timer?
		if (mTimer != null && lastPollMin == min)
			return;

		// Create New Timer & Schedule
		lastPollMin = min;
		if (mTimer != null)
			mTimer.cancel();
		mTimer = new Timer();
		mTimer.schedule(new PollTask(), 0, min * 60 * 1000);
		// mTimer.schedule(new PollTask(), 0, 10 * 1000);
		Log.i(LOGTAG, "Poll Interval = " + min + " minutes");
	}

	class PollTask extends TimerTask {

		@Override
		public void run() {
			Message msg = mServiceHandler.obtainMessage();
			msg.arg1 = ID_MSG_POLL;
			mServiceHandler.sendMessage(msg);
		}

	}

	// Handler that receives messages from the thread
	private final class ServiceHandler extends Handler {
		public ServiceHandler(Looper looper) {
			super(looper);
		}

		@Override
		public void handleMessage(Message msg) {
			switch (msg.arg1) {
			case ID_MSG_POLL:
				// Log.i(LOGTAG, "Poll Msg");
				CheckForNewThreads(false);
				break;
			case ID_MSG_DO_REFRESH:
				Log.i(LOGTAG, "ID_MSG_DO_REFRESH");
				CheckForNewThreads(true);
				break;

			case ID_MSG_DO_MARK_READ:
				Log.i(LOGTAG, "ID_MSG_DO_MARK_READ");
				MarkThreadRead(msg.arg2);
				break;

			case ID_MSG_DO_GET:
				Log.i(LOGTAG, "ID_MSG_DO_GET");
				SendCurrent();
				break;

			default:
				Log.i(LOGTAG, "WTF? Msg");
			}
		}
	}

	private boolean mUnreadOnly = true;
	private WPThread.List watched = null;
	private boolean hadError = false;

	private void BroadCastWatched(WPThread.List w) {
		if (mClient != null)
			mClient.UpdateWatchedThreads(w);
	}

	private void BroadCastProgressBar(boolean show) {
		if (mClient != null)
			mClient.ShowProgressBar(show);
	}

	private void SendCurrent() {
		// Check if we need to update unReadOnly status

		SharedPreferences sharedPrefs = PreferenceManager
				.getDefaultSharedPreferences(this);
		boolean unreadOnly = sharedPrefs.getBoolean("unread_only", true);
		if (mUnreadOnly == unreadOnly) {
			BroadCastWatched(watched);
			return;
		}

		mUnreadOnly = unreadOnly;
		if (unreadOnly) {
			// remove read ones
			for (int i = watched.size() - 1; i >= 0; i--) {
				if (watched.get(i).unread == 0)
					watched.remove(i);
			}
			BroadCastWatched(watched);
		} else {
			// Get new List
			CheckForNewThreads(true);
		}
	}

	private void CheckForNewThreads(boolean guiRequest) {
		try {
			// Update Preferences here
			SharedPreferences sharedPrefs = PreferenceManager
					.getDefaultSharedPreferences(this);
			String apiKey = sharedPrefs.getString("api_key", "");
			long pollInterval = Long.parseLong(sharedPrefs.getString(
					"poll_interval", "5"));
			SetPollInterval(pollInterval);

			boolean limitToWifi = sharedPrefs.getBoolean("limit_to_wifi", true);

			if (!guiRequest) {
				if (limitToWifi) {
					ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
					if (!cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI)
							.isConnected()) {
						Log.i(LOGTAG, "No Wireless Connection");
						return;
					}
				}
			}

			BroadCastProgressBar(true);
			try {
				// Two basic changes in thread status that we care about
				// 1 - Unread Message/Thread count has changed (less/more). -
				// Create/Update Notification
				// 2 - No Unread Messages - Cancel Notification. Can happen due
				// to other readers clearing them
				// Note:We never update the notification if the gui is active

				boolean unreadOnly = sharedPrefs
						.getBoolean("unread_only", true);
				boolean ignoreOwn = sharedPrefs.getBoolean("ignore_own_posts",
						true);

				WPThread.List w = WPThread.getWatched(apiKey, unreadOnly,
						ignoreOwn);

				boolean updated = (watched == null || WPThread.EqualMsgs(
						watched, w));
				watched = w;

				// clear any possible previous error notifications
				if (hadError) {
					hadError = false;
					cancelNotification(ID_NM_ERROR);
				}

				if (updated) {
					Log.i(LOGTAG, "Msg Update");
					// Always update client if msg list changed
					BroadCastWatched(w);
					if (mClient == null) {
						// No gui active
						// Notify user via system notification
						// Get Unread Count
						int nUnread = 0;
						int nThreads = 0;
						for (WPThread wpThread : watched) {
							if (wpThread.unread > 0) {
								nUnread += wpThread.unread;
								nThreads += 1;
							}
						}

						if (nUnread == 0) {
							// Cancel any notification
							cancelNotification(ID_NM_NEW_MSG);

						} else {
							// Update notification
							String msg = nUnread == 1 ? "1 new message"
									: nUnread + " new messages";
							msg += " in " + nThreads + " thread"
									+ (nThreads == 1 ? "" : "s");
							Log.i(LOGTAG, msg);
							DoNotification(ID_NM_NEW_MSG, msg);
						}
					}
				} else if (guiRequest) {
					// Forced Update
					BroadCastWatched(w);
				}

			} finally {
				BroadCastProgressBar(false);
			}

		} catch (WhirlpoolNullAPIException x) {
			Log.e(LOGTAG, "CheckForNewThreads", x);
			hadError = true;
			DoNotification(ID_NM_ERROR, x.getMessage(),
					WhirlMonPreferencesActivity.class);
		} catch (WhirlpoolException x) {
			Log.e(LOGTAG, "CheckForNewThreads", x);
			hadError = true;
			DoNotification(ID_NM_ERROR, x.getMessage());
		} catch (Throwable x) {
			Log.e(LOGTAG, "CheckForNewThreads", x);
		}
	}

	private void MarkThreadRead(long id) {
		try {
			// Update Preferences here
			SharedPreferences sharedPrefs = PreferenceManager
					.getDefaultSharedPreferences(this);
			String apiKey = sharedPrefs.getString("api_key", "");
			boolean unreadOnly = sharedPrefs.getBoolean("unread_only", true);
			try {
				WPThread.MarkThreadRead(apiKey, id);
				// find thread
				for (WPThread th : watched) {
					if (th.id == id) {
						// Mark read or removed
						if (unreadOnly)
							watched.remove(th);
						else
							th.unread = 0;
						return;
					}

				}
			} finally {
			}

		} catch (WhirlpoolNullAPIException x) {
			Log.e(LOGTAG, "CheckForNewThreads", x);
			hadError = true;
			DoNotification(ID_NM_ERROR, x.getMessage(),
					WhirlMonPreferencesActivity.class);
		} catch (WhirlpoolException x) {
			Log.e(LOGTAG, "CheckForNewThreads", x);
			hadError = true;
			DoNotification(ID_NM_ERROR, x.getMessage());
		} catch (Throwable x) {
			Log.e(LOGTAG, "CheckForNewThreads", x);
		}
	}

	void DoNotification(int id, String msg) {
		DoNotification(id, msg, MainActivity.class);
	}

	void DoNotification(int id, String msg, Class<?> activityClass) {
		// Generate Notification
		String ns = Context.NOTIFICATION_SERVICE;
		NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);

		int icon = (id == ID_NM_ERROR ? R.drawable.ic_error
				: R.drawable.ic_launcher);
		CharSequence tickerText = msg;
		long when = System.currentTimeMillis();
		Notification notification = new Notification(icon, tickerText, when);
		notification.flags |= Notification.FLAG_AUTO_CANCEL
				| Notification.FLAG_ONLY_ALERT_ONCE;

		Context context = getApplicationContext();
		CharSequence contentTitle = "WhirlMon";
		CharSequence contentText = msg;
		Intent notificationIntent = new Intent(this, activityClass);

		PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
				notificationIntent, 0);

		notification.setLatestEventInfo(context, contentTitle, contentText,
				contentIntent);

		mNotificationManager.notify(id, notification);
	}

	void cancelNotification(int id) {
		String ns = Context.NOTIFICATION_SERVICE;
		NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
		mNotificationManager.cancel(id);
	}
}
